# Copyright 2020 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

load("@com_google_protobuf//bazel:java_lite_proto_library.bzl", "java_lite_proto_library")
load("@com_google_protobuf//bazel:java_proto_library.bzl", "java_proto_library")
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_python//python:proto.bzl", "py_proto_library")
load("@rules_python//sphinxdocs:sphinx_docs_library.bzl", "sphinx_docs_library")
load("//pw_bloat:pw_size_diff.bzl", "pw_size_diff")
load("//pw_bloat:pw_size_table.bzl", "pw_size_table")
load("//pw_build:compatibility.bzl", "incompatible_with_mcu")
load("//pw_build:copy_to_bin.bzl", "copy_to_bin")
load(
    "//pw_protobuf_compiler:pw_proto_library.bzl",
    "nanopb_proto_library",
    "nanopb_rpc_proto_library",
    "pw_proto_filegroup",
    "pwpb_proto_library",
    "pwpb_rpc_proto_library",
    "raw_rpc_proto_library",
)
load("//pw_unit_test:pw_cc_test.bzl", "pw_cc_test")

package(
    default_visibility = ["//visibility:public"],
    features = ["-layering_check"],
)

licenses(["notice"])

pw_proto_filegroup(
    name = "benchmark_proto_and_options",
    srcs = ["benchmark.proto"],
    options_files = [
        "benchmark.options",
        "benchmark.pwpb_options",
    ],
)

proto_library(
    name = "benchmark_proto",
    srcs = [":benchmark_proto_and_options"],
)

pwpb_proto_library(
    name = "benchmark_pwpb",
    deps = [":benchmark_proto"],
)

raw_rpc_proto_library(
    name = "benchmark_raw_rpc",
    deps = [":benchmark_proto"],
)

py_proto_library(
    name = "benchmark_py_pb2",
    deps = [":benchmark_proto"],
)

cc_library(
    name = "benchmark",
    srcs = ["benchmark.cc"],
    hdrs = ["public/pw_rpc/benchmark.h"],
    strip_include_prefix = "public",
    deps = [
        ":benchmark_pwpb",
        ":benchmark_raw_rpc",
    ],
)

pw_cc_test(
    name = "benchmark_service_test",
    srcs = ["benchmark_service_test.cc"],
    features = ["-conversion_warnings"],
    deps = [
        ":benchmark",
        ":internal_test_utils",
        "//pw_rpc:pw_rpc_test_raw_rpc",
        "//pw_rpc/raw:client_testing",
        "//pw_rpc/raw:test_method_context",
    ],
)

# TODO: b/242059613 - Build this as a cc_binary and use it in integration tests.
filegroup(
    name = "test_rpc_server",
    srcs = ["test_rpc_server.cc"],
    # deps = [
    #     "system_server",
    #     ":benchmark",
    #     "//pw_log",
    # ],
)

cc_library(
    name = "client_server",
    srcs = ["client_server.cc"],
    hdrs = ["public/pw_rpc/client_server.h"],
    strip_include_prefix = "public",
    deps = [":pw_rpc"],
)

# See https://pigweed.dev/pw_rpc/cpp.html#c.PW_RPC_USE_GLOBAL_MUTEX for documentation.
constraint_setting(
    name = "use_global_mutex",
    default_constraint_value = ":use_global_mutex_true",
)

constraint_value(
    name = "use_global_mutex_false",
    constraint_setting = ":use_global_mutex",
)

constraint_value(
    name = "use_global_mutex_true",
    constraint_setting = ":use_global_mutex",
)

# See https://pigweed.dev/pw_rpc/cpp.html#c.PW_RPC_YIELD_MODE for documentation.
constraint_setting(
    name = "yield_mode",
    default_constraint_value = ":yield_mode_sleep",
)

constraint_value(
    name = "yield_mode_busy_loop",
    constraint_setting = ":yield_mode",
)

constraint_value(
    name = "yield_mode_sleep",
    constraint_setting = ":yield_mode",
)

constraint_value(
    name = "yield_mode_yield",
    constraint_setting = ":yield_mode",
)

cc_library(
    name = "pw_rpc",
    srcs = [
        "call.cc",
        "channel.cc",
        "channel_list.cc",
        "client.cc",
        "client_call.cc",
        "endpoint.cc",
        "packet.cc",
        "packet_meta.cc",
        "server.cc",
        "server_call.cc",
        "service.cc",
    ],
    hdrs = [
        "public/pw_rpc/channel.h",
        "public/pw_rpc/client.h",
        "public/pw_rpc/internal/call.h",
        "public/pw_rpc/internal/call_context.h",
        "public/pw_rpc/internal/channel_list.h",
        "public/pw_rpc/internal/client_call.h",
        "public/pw_rpc/internal/config.h",
        "public/pw_rpc/internal/encoding_buffer.h",
        "public/pw_rpc/internal/endpoint.h",
        "public/pw_rpc/internal/grpc.h",
        "public/pw_rpc/internal/hash.h",
        "public/pw_rpc/internal/lock.h",
        "public/pw_rpc/internal/log_config.h",
        "public/pw_rpc/internal/method.h",
        "public/pw_rpc/internal/method_info.h",
        "public/pw_rpc/internal/method_lookup.h",
        "public/pw_rpc/internal/method_union.h",
        "public/pw_rpc/internal/packet.h",
        "public/pw_rpc/internal/server_call.h",
        "public/pw_rpc/internal/service_client.h",
        "public/pw_rpc/method_id.h",
        "public/pw_rpc/method_info.h",
        "public/pw_rpc/method_type.h",
        "public/pw_rpc/packet_meta.h",
        "public/pw_rpc/server.h",
        "public/pw_rpc/service.h",
        "public/pw_rpc/service_id.h",
        "public/pw_rpc/writer.h",
    ],
    # LINT.IfChange
    defines = select({
        ":yield_mode_busy_loop": ["PW_RPC_YIELD_MODE=PW_RPC_YIELD_MODE_BUSY_LOOP"],
        ":yield_mode_sleep": ["PW_RPC_YIELD_MODE=PW_RPC_YIELD_MODE_SLEEP"],
        ":yield_mode_yield": ["PW_RPC_YIELD_MODE=PW_RPC_YIELD_MODE_YIELD"],
    }) + select({
        ":use_global_mutex_false": ["PW_RPC_USE_GLOBAL_MUTEX=0"],
        ":use_global_mutex_true": ["PW_RPC_USE_GLOBAL_MUTEX=1"],
    }),
    # LINT.ThenChange(//pw_rpc/public/pw_rpc/internal/config.h)
    implementation_deps = ["//pw_assert:check"],
    strip_include_prefix = "public",
    deps = [
        ":config_override",
        ":internal_packet_pwpb",
        "//pw_assert:assert",
        "//pw_bytes",
        "//pw_containers:intrusive_list",
        "//pw_function",
        "//pw_log",
        "//pw_polyfill",
        "//pw_preprocessor",
        "//pw_result",
        "//pw_span",
        "//pw_status",
        "//pw_sync:lock_annotations",
        "//pw_toolchain:no_destructor",
    ] + select({
        ":yield_mode_busy_loop": [],
        ":yield_mode_sleep": ["//pw_thread:sleep"],
        ":yield_mode_yield": ["//pw_thread:yield"],
    }) + select({
        ":use_global_mutex_false": [],
        ":use_global_mutex_true": ["//pw_sync:mutex"],
    }),
)

label_flag(
    name = "config_override",
    build_setting_default = "//pw_build:default_module_config",
)

cc_library(
    name = "completion_request_callback_config_enabled",
    defines = [
        "PW_RPC_COMPLETION_REQUEST_CALLBACK=1",
    ],
)

config_setting(
    name = "completion_request_callback_config_setting",
    flag_values = {
        ":config_override": ":completion_request_callback_config_enabled",
    },
)

cc_library(
    name = "synchronous_client_api",
    hdrs = [
        "public/pw_rpc/internal/synchronous_call_impl.h",
        "public/pw_rpc/synchronous_call.h",
        "public/pw_rpc/synchronous_call_result.h",
    ],
    strip_include_prefix = "public",
    tags = ["noclangtidy"],
    deps = [
        ":pw_rpc",
        "//pw_assert:assert",
        "//pw_chrono:system_clock",
        "//pw_sync:timed_thread_notification",
    ],
)

cc_library(
    name = "client_server_testing",
    hdrs = ["public/pw_rpc/internal/client_server_testing.h"],
    strip_include_prefix = "public",
    deps = [
        ":client_server",
        ":internal_test_utils",
        "//pw_bytes",
        "//pw_result",
    ],
)

cc_library(
    name = "client_server_testing_threaded",
    hdrs = ["public/pw_rpc/internal/client_server_testing_threaded.h"],
    strip_include_prefix = "public",
    deps = [
        ":client_server_testing",
        "//pw_bytes",
        "//pw_result",
        "//pw_sync:binary_semaphore",
        "//pw_sync:lock_annotations",
        "//pw_sync:mutex",
        "//pw_thread:thread",
    ],
)

cc_library(
    name = "test_helpers",
    hdrs = ["public/pw_rpc/test_helpers.h"],
    strip_include_prefix = "public",
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
        "//pw_assert:assert",
        "//pw_chrono:system_clock",
        "//pw_status",
        "//pw_sync:counting_semaphore",
        "//pw_thread:yield",
    ],
)

# thread_testing target is kept for backward compatibility.
# New code should use test_helpers instead.
cc_library(
    name = "thread_testing",
    hdrs = ["public/pw_rpc/thread_testing.h"],
    strip_include_prefix = "public",
    deps = [":test_helpers"],
)

cc_library(
    name = "internal_test_utils",
    srcs = ["fake_channel_output.cc"],
    hdrs = [
        "public/pw_rpc/internal/fake_channel_output.h",
        "public/pw_rpc/internal/method_impl_tester.h",
        "public/pw_rpc/internal/method_info_tester.h",
        "public/pw_rpc/internal/test_method_context.h",
        "public/pw_rpc/internal/test_utils.h",
        "public/pw_rpc/payloads_view.h",
        "pw_rpc_private/fake_server_reader_writer.h",
        "pw_rpc_private/test_method.h",
    ],
    implementation_deps = ["//pw_assert:check"],
    includes = [
        ".",
        "public",
    ],
    tags = ["noclangtidy"],
    visibility = [":__subpackages__"],
    deps = [
        ":pw_rpc",
        "//pw_assert:assert",
        "//pw_bytes",
        "//pw_containers:filtered_view",
        "//pw_containers:vector",
        "//pw_containers:wrapped_iterator",
        "//pw_rpc/raw:fake_channel_output",
        "//pw_span",
        "//pw_sync:mutex",
    ],
)

cc_library(
    name = "integration_testing",
    testonly = True,
    srcs = [
        "integration_testing.cc",
    ],
    hdrs = [
        "public/pw_rpc/integration_test_socket_client.h",
        "public/pw_rpc/integration_testing.h",
    ],
    features = ["-conversion_warnings"],
    strip_include_prefix = "public",
    deps = [
        ":pw_rpc",
        "//pw_hdlc",
        "//pw_hdlc:default_addresses",
        "//pw_hdlc:rpc_channel_output",
        "//pw_log",
        "//pw_stream:socket_stream",
        "//pw_unit_test",
        "//pw_unit_test:logging",
    ],
)

# TODO: b/242059613 - Add the client integration test to the build.
filegroup(
    name = "client_integration_test",
    srcs = ["client_integration_test.cc"],
)

pw_cc_test(
    name = "call_test",
    srcs = [
        "call_test.cc",
    ],
    features = ["-conversion_warnings"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
    ],
)

pw_cc_test(
    name = "callback_test",
    srcs = ["callback_test.cc"],
    features = ["-conversion_warnings"],
    deps = [
        ":pw_rpc",
        ":pw_rpc_test_raw_rpc",
        "//pw_rpc/raw:client_testing",
        "//pw_sync:binary_semaphore",
        "//pw_thread:non_portable_test_thread_options",
        "//pw_thread:sleep",
        "//pw_thread:yield",
        "//pw_thread_stl:non_portable_test_thread_options",
    ],
)

pw_cc_test(
    name = "channel_list_test",
    srcs = ["channel_list_test.cc"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
    ],
)

pw_cc_test(
    name = "channel_test",
    srcs = ["channel_test.cc"],
    features = ["-conversion_warnings"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
    ],
)

pw_cc_test(
    name = "method_test",
    srcs = ["method_test.cc"],
    features = ["-conversion_warnings"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
    ],
)

pw_cc_test(
    name = "packet_test",
    srcs = [
        "packet_test.cc",
    ],
    features = [
        "-conversion_warnings",
        "-ctad_warnings",
    ],
    deps = [
        ":pw_rpc",
        "//pw_fuzzer:fuzztest",
    ],
)

pw_cc_test(
    name = "packet_meta_test",
    srcs = [
        "packet_meta_test.cc",
    ],
    features = [
        "-conversion_warnings",
        "-ctad_warnings",
    ],
    deps = [
        ":pw_rpc",
        "//pw_fuzzer:fuzztest",
    ],
)

pw_cc_test(
    name = "client_server_test",
    srcs = ["client_server_test.cc"],
    features = ["-conversion_warnings"],
    deps = [
        ":client_server",
        ":internal_test_utils",
        "//pw_rpc/raw:server_api",
    ],
)

pw_cc_test(
    name = "server_test",
    srcs = [
        "server_test.cc",
    ],
    features = ["-conversion_warnings"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
        "//pw_assert:check",
    ],
)

pw_cc_test(
    name = "service_test",
    srcs = [
        "service_test.cc",
    ],
    features = ["-conversion_warnings"],
    deps = [
        ":internal_test_utils",
        ":pw_rpc",
    ],
)

pw_cc_test(
    name = "fake_channel_output_test",
    srcs = ["fake_channel_output_test.cc"],
    features = ["-conversion_warnings"],
    deps = [":internal_test_utils"],
)

pw_cc_test(
    name = "test_helpers_test",
    srcs = ["test_helpers_test.cc"],
    features = [
        "-conversion_warnings",
        "-ctad_warnings",
    ],
    deps = [
        ":test_helpers",
        "//pw_containers:vector",
        "//pw_result",
        "//pw_rpc/pwpb:client_testing",
        "//pw_rpc/pwpb:echo_service",
        "//pw_rpc/pwpb:server_api",
        "//pw_status",
        "//pw_sync:interrupt_spin_lock",
        "//pw_sync:lock_annotations",
        "//pw_sync:timed_thread_notification",
    ],
)

proto_library(
    name = "internal_packet_proto",
    srcs = ["internal/packet.proto"],
)

java_proto_library(
    name = "packet_proto_java",
    deps = [":internal_packet_proto"],
)

java_lite_proto_library(
    name = "packet_proto_java_lite",
    deps = [":internal_packet_proto"],
)

py_proto_library(
    name = "internal_packet_proto_pb2",
    deps = [":internal_packet_proto"],
)

pwpb_proto_library(
    name = "internal_packet_pwpb",
    deps = [":internal_packet_proto"],
)

proto_library(
    name = "pw_rpc_test_proto",
    srcs = [
        "pw_rpc_test_protos/no_package.proto",
        "pw_rpc_test_protos/test.proto",
    ],
    strip_import_prefix = "/pw_rpc",
)

nanopb_proto_library(
    name = "pw_rpc_test_nanopb",
    deps = [":pw_rpc_test_proto"],
)

nanopb_rpc_proto_library(
    name = "pw_rpc_test_nanopb_rpc",
    nanopb_proto_library_deps = [":pw_rpc_test_nanopb"],
    deps = [":pw_rpc_test_proto"],
)

pwpb_proto_library(
    name = "pw_rpc_test_pwpb",
    deps = [":pw_rpc_test_proto"],
)

pwpb_rpc_proto_library(
    name = "pw_rpc_test_pwpb_rpc",
    pwpb_proto_library_deps = [":pw_rpc_test_pwpb"],
    deps = [":pw_rpc_test_proto"],
)

raw_rpc_proto_library(
    name = "pw_rpc_test_raw_rpc",
    deps = [":pw_rpc_test_proto"],
)

pw_proto_filegroup(
    name = "echo_proto_and_options",
    srcs = ["echo.proto"],
    options_files = [
        "echo.options",
        "echo.pwpb_options",
    ],
)

proto_library(
    name = "echo_proto",
    srcs = [":echo_proto_and_options"],
)

py_proto_library(
    name = "echo_py_pb2",
    deps = [":echo_proto"],
)

nanopb_proto_library(
    name = "echo_nanopb",
    deps = [":echo_proto"],
)

nanopb_rpc_proto_library(
    name = "echo_nanopb_rpc",
    nanopb_proto_library_deps = [":echo_nanopb"],
    deps = [":echo_proto"],
)

pwpb_proto_library(
    name = "echo_pwpb",
    deps = [":echo_proto"],
)

pwpb_rpc_proto_library(
    name = "echo_pwpb_rpc",
    pwpb_proto_library_deps = [":echo_pwpb"],
    deps = [":echo_proto"],
)

filegroup(
    name = "doxygen",
    srcs = [
        "public/pw_rpc/benchmark.h",
        "public/pw_rpc/channel.h",
        "public/pw_rpc/internal/config.h",
        "public/pw_rpc/synchronous_call.h",
    ],
)

pw_size_diff(
    name = "server_only_size_diff",
    base = "//pw_rpc/size_report:base",
    label = "Server by itself",
    target = "//pw_rpc/size_report:server_only",
)

pw_size_diff(
    name = "server_with_echo_service_size_diff",
    base = "//pw_rpc/size_report:base_with_nanopb",
    label = "Server with a registered nanopb EchoService",
    target = "//pw_rpc/size_report:server_with_echo_service",
)

pw_size_table(
    name = "server_size",
    reports = [
        ":server_only_size_diff",
        ":server_with_echo_service_size_diff",
    ],
)

sphinx_docs_library(
    name = "docs",
    srcs = [
        "Kconfig",
        "benchmark.proto",
        "cpp.rst",
        "design.rst",
        "docs.rst",
        "echo.proto",
        "guides.rst",
        "internal/packet.proto",
        "libraries.rst",
        "protocol.rst",
        ":server_size",
        "//pw_rpc/nanopb:docs",
        "//pw_rpc/pwpb:docs",
        "//pw_rpc/py:docs",
        "//pw_rpc/ts:docs",
    ],
    prefix = "pw_rpc/",
    target_compatible_with = incompatible_with_mcu(),
)

copy_to_bin(
    name = "js_protos",
    srcs = [
        "echo.proto",
        "internal/packet.proto",
    ],
)
